using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Jeremi
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //******* Variables ***********

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public MouseState LastMouse;
        public MouseState CurrentMouse;

        public KeyboardState LastKeyboard;
        public KeyboardState CurrentKeyboard;

        Vector3 cameraPosition;
        Vector3 cameraTarget;
        Vector3 cameraUp;
        Vector3 cameraDirection;
        Matrix viewMatrix;

        float fieldOfView;
        float aspectRatio;
        float farClip;
        float nearClip;
        Matrix projectionMatrix;

        Model ball;
        Matrix ball_worldMatrix;

        float moveAmount;
        float rotationAmount;

        Effect skyboxShader;
        TextureCube skyboxTexture;
        Model skyboxMesh;

        Vector3 skyboxPosition;
        Quaternion skyboxRotation;
        Vector3 skyboxScale;

        //******** Game Methods ************

        private void SetUpWindow()
        {
            graphics.PreferredBackBufferHeight = 720;
            graphics.PreferredBackBufferWidth = 1024;
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24;
            graphics.MinimumPixelShaderProfile = ShaderProfile.PS_2_0;
            graphics.MinimumVertexShaderProfile = ShaderProfile.VS_2_0;
            graphics.PreferredBackBufferFormat = SurfaceFormat.Bgr32;
            graphics.SynchronizeWithVerticalRetrace = true;
            graphics.PreferMultiSampling = true;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();

            Window.Title = "Jeremi's Game";
        }

        private void SetUpCamera()
        {
            cameraPosition = new Vector3(0f, 5f, 400f);
            cameraTarget = Vector3.Zero;
            cameraUp = Vector3.Up; //UnitY 0,1,0
            UpdateCamera();

            fieldOfView = MathHelper.ToRadians(45f); //PiOver4
            aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width /
                          (float)graphics.GraphicsDevice.Viewport.Height;

            nearClip = 1f;
            farClip = 10000f;
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, nearClip, farClip);

            moveAmount = 5f;
            rotationAmount = MathHelper.ToRadians(2f);
            cameraDirection = -Vector3.UnitZ;
        }

        private void SetUpSkybox()
        {
            skyboxPosition = new Vector3(0, 0, 0);
            skyboxRotation = new Quaternion(0, 0, 0, 1);
            skyboxScale = new Vector3(55, 55, 55);

            skyboxMesh = Content.Load<Model>("Models/skybox");
            skyboxShader = Content.Load<Effect>("Shaders/skybox");
            skyboxTexture = Content.Load<TextureCube>("Textures/cubeMap"); ;
        }


        private void UpdateCamera()
        {
            cameraTarget = cameraPosition + cameraDirection;
            
            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraTarget, cameraUp);
        }


        private void DrawSkyBox(GameTime gameTime)
        {
            Matrix World = Matrix.CreateScale(skyboxScale) *
                            Matrix.CreateFromQuaternion(skyboxRotation) *
                            Matrix.CreateTranslation(cameraPosition);

            skyboxShader.Parameters["World"].SetValue(World);
            skyboxShader.Parameters["View"].SetValue(viewMatrix);
            skyboxShader.Parameters["Projection"].SetValue(projectionMatrix);
            skyboxShader.Parameters["surfaceTexture"].SetValue(skyboxTexture);

            skyboxShader.Parameters["EyePosition"].SetValue(cameraPosition);

            for (int pass = 0; pass < skyboxShader.CurrentTechnique.Passes.Count; pass++)
            {
                for (int msh = 0; msh < skyboxMesh.Meshes.Count; msh++)
                {
                    ModelMesh mesh = skyboxMesh.Meshes[msh];
                    for (int prt = 0; prt < mesh.MeshParts.Count; prt++)
                        mesh.MeshParts[prt].Effect = skyboxShader;
                    mesh.Draw();
                }
            }
        }

        private void DrawModel(Model model, Matrix model_worldMatrix)
        {
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = model_worldMatrix;
                    effect.View = viewMatrix;
                    effect.Projection = projectionMatrix;
                }
                mesh.Draw();
            }
        }


        private void ProcessMouse()
        {
            if (CurrentMouse.X != LastMouse.X)
            {
                int MoveLength = LastMouse.X - CurrentMouse.X;

                cameraDirection = Vector3.Transform(cameraDirection, Matrix.CreateRotationY(MathHelper.ToRadians(MoveLength)));
            }

            if (CurrentMouse.Y != LastMouse.Y)
            {
                int MoveLength = LastMouse.Y - CurrentMouse.Y;
                //cameraDirection = Vector3.Cross(cameraDirection, cameraUp);
            }
            
        }

        private void ProcessKeyboard()
        {
            if (CurrentKeyboard.IsKeyDown(Keys.Q))
                cameraPosition.Y += moveAmount;

            if (CurrentKeyboard.IsKeyDown(Keys.E))
                cameraPosition.Y -= moveAmount;

            if (CurrentKeyboard.IsKeyDown(Keys.W))
                cameraPosition += cameraDirection * moveAmount;

            if (CurrentKeyboard.IsKeyDown(Keys.S))
                cameraPosition -= cameraDirection * moveAmount;

            Vector3 perpDirection;
            perpDirection = Vector3.Transform(cameraDirection,
                            Matrix.CreateRotationY(MathHelper.PiOver2));

            if (CurrentKeyboard.IsKeyDown(Keys.A))
                cameraPosition += perpDirection * moveAmount;

            if (CurrentKeyboard.IsKeyDown(Keys.D))
                cameraPosition -= perpDirection * moveAmount;


            if (CurrentKeyboard.IsKeyDown(Keys.Z))
                cameraDirection = Vector3.Transform(cameraDirection, Matrix.CreateRotationY(rotationAmount));

            if (CurrentKeyboard.IsKeyDown(Keys.C))
                cameraDirection = Vector3.Transform(cameraDirection, Matrix.CreateRotationY(-rotationAmount));
        }


        //******** XNA Methods *************

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            SetUpWindow();

            SetUpCamera();

            SetUpSkybox();

            //Set the Mouse States
            LastMouse = Mouse.GetState();
            CurrentMouse = LastMouse;

            //Set the keyboard states
            LastKeyboard = Keyboard.GetState();
            CurrentKeyboard = LastKeyboard;

            base.Initialize();
        }


        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            ball = Content.Load<Model>("Models/basketball");
            ball_worldMatrix = Matrix.CreateScale(0.3f);
        }

        protected override void UnloadContent()
        {
            Content.Unload();
        }


        protected override void Update(GameTime gameTime)
        {
            CurrentMouse = Mouse.GetState();
            CurrentKeyboard = Keyboard.GetState();

            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            if (Keyboard.GetState().IsKeyDown(Keys.F))
                graphics.ToggleFullScreen();

            ProcessKeyboard();

            ProcessMouse();

            UpdateCamera();
            
            LastMouse = CurrentMouse;
            LastKeyboard = CurrentKeyboard;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            DrawModel(ball, ball_worldMatrix);

            DrawSkyBox(gameTime);

            base.Draw(gameTime);
        }
    }
}
